﻿using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace Mars.GpuInstance
{
    public class InstanceRuntimeData :IRecyclable,IDisposable
    {
        public static int maximumLODLevel = 0; //指定lod
        
        
        public NativeList<float4x4> input;
        private NativeParallelHashMap<int, int> idToIndexMap; //id和序号映射
        private NativeParallelHashMap<int, int> indexToIdMap; //序号和id映射
        public NativeList<float4> campColorData; //xyz表示颜色，w表示透明度
        public NativeList<int3> showInfo; //x特殊裁剪距离（0不使用特殊裁剪距离）  y不需要建筑范围裁剪的id(1忽略裁剪，其他不忽略)  z是否显示（1显示，其他不显示）
        public NativeArray<int> lodInfo;
        public NativeArray<int> lodRange;
        public NativeArray<bool> viewIndex;
        
        public bool drawable = true;

        public int clearTimeFrame = 60; //如果数量清空了，60帧后清除

        //数据是否有更新
        public bool dataModified = false;
        public bool forceRender = false;
        private bool hasCampData = false;

        private float3 boxCenter; //包围盒的中心
        private float3 boxExtents; //包围盒的范围
        private float2 showRange; //显示的视距范围

        public List<InstanceRenderBatch> lodRenderBatchs = new List<InstanceRenderBatch>();
        public List<InstanceRenderBatch> shadowBatchs = new List<InstanceRenderBatch>();

        private bool castShadow = true;
        private ShadowLod shadowLod;

        
        public InstanceRuntimeData(float3 _boxCenter, float3 _boxExtents)
        {
            InitContainer();
            clearTimeFrame = int.MaxValue;
            dataModified = false;
            boxCenter = _boxCenter;
            boxExtents = _boxExtents;
        }

        public InstanceRuntimeData()
        {
            InitContainer();
            clearTimeFrame = int.MaxValue;
            dataModified = false;
        }
        
        public void InitContainer()
        {
            if(!input.IsCreated) input = new NativeList<float4x4>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            if(!idToIndexMap.IsCreated) idToIndexMap = new NativeParallelHashMap<int, int>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            if(!indexToIdMap.IsCreated) indexToIdMap = new NativeParallelHashMap<int, int>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            if(!campColorData.IsCreated) campColorData = new NativeList<float4>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            if(!showInfo.IsCreated) showInfo = new NativeList<int3>(InstancerConstants.ADD_COUNT, Allocator.Persistent);
            lodRenderBatchs.Clear();
            shadowBatchs.Clear();
        }

        public void UpdateShowRange(float2 r)
        {
            showRange = r;
        }

        public void UpdateBox(float3 _boxCenter, float3 _boxExtents)
        {
            boxCenter = _boxCenter;
            boxExtents = _boxExtents;
        }

        public void AddBatch(InstanceRenderBatch batch)
        {
            lodRenderBatchs.Add(batch);
        }

        public void AddShadowBatch(InstanceRenderBatch batch)
        {
            shadowBatchs.Add(batch);
        }

        public void UpdateShadowLod(ShadowLod s)
        {
            shadowLod = s;
        }

        public int GetInstanceCount()
        {
            if (input.IsCreated)
            {
                return input.Length;
            }
            return 0;
        }
        
        #region 添加删除实体

        public void AddInstance(int id, float4x4 matrix, bool ignoreCullBound = false, int cullingDis = 0,
            float4 campColor = default)
        {
            var campCompare = campColor != default;
            var validCampData = campCompare.x || campCompare.y || campCompare.z || campCompare.w;
            var colorData = validCampData ? campColor : new float4(1, 1, 1, 1);
            if (validCampData && !hasCampData)
            {
                foreach (var batch in lodRenderBatchs)
                {
                    batch.EnableKeyWord("_USE_INSTANCING_BUFFER");
                }
            }

            hasCampData |= validCampData;

            if (idToIndexMap.TryGetValue(id, out var index))
            {
                showInfo[index] = new int3(cullingDis, ignoreCullBound ? 1 : 0, 1);
                input[index] = matrix;

                if (validCampData)
                {
                    campColorData[index] = campColor;
                }
            }
            else
            {
                idToIndexMap.TryAdd(id, input.Length);
                indexToIdMap.TryAdd(input.Length, id);
                input.Add(matrix);

                campColorData.Add(colorData);
                showInfo.Add(new int3(cullingDis, ignoreCullBound ? 1 : 0, 1));
            }

            clearTimeFrame = int.MaxValue;
            dataModified = true;
        }

        public void RemoveInstance(int id)
        {
            if (!idToIndexMap.TryGetValue(id, out var index))
            {
                return;
            }

            int lastIndex = input.Length - 1;
            if (indexToIdMap.TryGetValue(lastIndex, out var lastId)) //找到最后一个
            {
                input[index] = input[lastIndex]; //把最后一个替换到当前index
                input.RemoveAt(lastIndex); //把最后一个删掉

                campColorData[index] = campColorData[lastIndex];
                campColorData.RemoveAt(lastIndex);

                showInfo[index] = showInfo[lastIndex];
                showInfo.RemoveAt(lastIndex);

                idToIndexMap[lastId] = index;
                indexToIdMap[index] = lastId;

                idToIndexMap.Remove(id);
                indexToIdMap.Remove(lastIndex);
            }

            dataModified = true;

            //记录清空时的帧数
            if (input.Length <= 0)
            {
                clearTimeFrame = Time.frameCount;
            }
        }

        public void ClearInstance()
        {
            input.Clear();
            idToIndexMap.Clear();
            indexToIdMap.Clear();
            dataModified = true;

            //记录清空时的帧数
            if (input.Length <= 0)
            {
                clearTimeFrame = Time.frameCount;
            }
        }
        
        
        public void UpdateSpecCullingDis(int id, int dis)
        {
            if (idToIndexMap.TryGetValue(id, out var index))
            {
                var info = showInfo[index];
                info.x = dis;
                showInfo[index] = info;
                dataModified = true;
            }
        }
        
        public void ShowInstance(int id, bool show)
        {
            if (idToIndexMap.TryGetValue(id, out var index))
            {
                var info = showInfo[index];
                info.z = show? 1 : 0;
                showInfo[index] = info;
                dataModified = true;
            }
        }

        #endregion

        #region 计算相关
        
        public void VisibilityCalculation(NativeArray<float4> frustumPlanes, float3 cameraPos,
            NativeList<JobHandle> handles, NativeList<RefSimpleBound> cullBounds)
        {
            dataModified = false;
            var len = input.Length;
            if (len <= 0)
            {
                foreach (var renderData in lodRenderBatchs)
                {
                    renderData.ClearDrawCount();
                }

                foreach (var renderData in shadowBatchs)
                {
                    renderData.ClearDrawCount();
                }

                return;
            }
            
            if (!viewIndex.IsCreated)
            {
                viewIndex = new NativeArray<bool>(len, Allocator.Persistent);
            }
            else if (viewIndex.IsCreated && viewIndex.Length < len)
            {
                viewIndex.Dispose();
                viewIndex = new NativeArray<bool>(len, Allocator.Persistent);
            }

            if (!lodInfo.IsCreated)
            {
                lodInfo = new NativeArray<int>(len, Allocator.Persistent);
            }
            else if (lodInfo.IsCreated && lodInfo.Length < len)
            {
                lodInfo.Dispose();
                lodInfo = new NativeArray<int>(len, Allocator.Persistent);
            }

            var job = new CullingJob
            {
                input = input,
                speceShowInfo = showInfo,
                boxCenter = boxCenter,
                boxExtents = boxExtents,
                cameraPlanes = frustumPlanes,
                maximumLODLevel = maximumLODLevel,
                forceRender = forceRender,
                cullBounds = cullBounds,
                lodRange = lodRange,
                lodInfo = lodInfo,
                viewIndex = viewIndex,
                cameraPos = cameraPos
            };

            var cullingJobHandle = job.Schedule(len, 64);
            
            foreach (var renderData in lodRenderBatchs)
            {
                var tmpHandle = renderData.CopyData(this, false, cullingJobHandle);
                handles.Add(tmpHandle);
            }
            
            foreach (var renderData in shadowBatchs)
            {
                var tmpHandle = renderData.CopyData(this, true, cullingJobHandle);
                handles.Add(tmpHandle);
            }
        }
        
        #endregion


        #region 绘制相关

        public void Draw(bool shadow = true)
        {
            if (!drawable)
            {
                return;
            }
            shadow = castShadow && shadow;
            DrawMeshInstanced(shadow);
        }
        
        private void DrawMeshInstanced(bool castShadow = true)
        {
            var castModel = ShadowCastingMode.Off;
            if (castShadow && shadowLod == ShadowLod.FOLLOW_LOD)
            {
                castModel = ShadowCastingMode.On;
            }
            
            //倒叙遍历lodRenderBatchs
            for (int i = lodRenderBatchs.Count - 1; i >= 0; i--)
            {
                var batch = lodRenderBatchs[i];
                if (batch.GetInstanceCount() <= 0)
                {
                    continue;
                }

                batch.Draw(castModel, hasCampData);
            }

            if (castShadow && shadowLod != ShadowLod.FOLLOW_LOD)
            {
                for (int i = 0; i < shadowBatchs.Count; i++)
                {
                    var batch = shadowBatchs[i];
                    if (batch.GetInstanceCount() <= 0)
                    {
                        continue;
                    }
                    
                    batch.Draw(ShadowCastingMode.ShadowsOnly, hasCampData);
                }
            }
        }

        #endregion

        #region 释放回收

        public void Dispose()
        {
            if (input.IsCreated)
            {
                input.Dispose();
            }
            
            if (idToIndexMap.IsCreated)
            {
                idToIndexMap.Dispose();
            }

            if (indexToIdMap.IsCreated)
            {
                indexToIdMap.Dispose();
            }
            
            if (campColorData.IsCreated)
            {
                campColorData.Dispose();
            }

            if (showInfo.IsCreated)
            {
                showInfo.Dispose();
            }

            if (lodInfo.IsCreated)
            {
                lodInfo.Dispose();
            }

            if (lodRange.IsCreated)
            {
                lodRange.Dispose();
            }

            if (viewIndex.IsCreated)
            {
                viewIndex.Dispose();
            }

            foreach (var renderData in lodRenderBatchs)
            {
                renderData.Dispose();
            }
            lodRenderBatchs.Clear();

            foreach (var renderData in shadowBatchs)
            {
                renderData.Dispose();
            }
            shadowBatchs.Clear();
            hasCampData = false;
        }

        public void OnRecycle()
        {

            
            if (input.IsCreated)
            {
                input.Dispose();
            }
            
            if (idToIndexMap.IsCreated)
            {
                idToIndexMap.Dispose();
            }

            if (indexToIdMap.IsCreated)
            {
                indexToIdMap.Dispose();
            }
            
            if (campColorData.IsCreated)
            {
                campColorData.Dispose();
            }

            if (showInfo.IsCreated)
            {
                showInfo.Dispose();
            }

            if (lodInfo.IsCreated)
            {
                lodInfo.Dispose();
            }

            if (lodRange.IsCreated)
            {
                lodRange.Dispose();
            }

            if (viewIndex.IsCreated)
            {
                viewIndex.Dispose();
            }

            if (lodInfo.IsCreated)
            {
                lodInfo.Dispose();
            }

            var cacheDataManager = InstanceCacheDataManager.GetInstance();
            foreach (var renderData in lodRenderBatchs)
            {
                cacheDataManager.ReturnInstanceRenderBatch(renderData);
            }
            lodRenderBatchs.Clear();

            foreach (var renderData in shadowBatchs)
            {
                cacheDataManager.ReturnInstanceRenderBatch(renderData);
            }
            shadowBatchs.Clear();
            hasCampData = false;
        }
        
        #endregion
    }
}